import chalk, { Color } from 'chalk'
import { createHash, createHmac } from 'crypto'
import { difference } from 'lodash-es'
import { builtinModules } from 'module'
import path from 'path'
import { normalizePath } from 'vite'
import { CWD } from './constants'

const _console: any = { ...console }

const createLog = (method: string, id: string, color: typeof Color & keyof typeof chalk) => {
  const _id = chalk.bold[color](`[${id}]`)
  return (...args: any[]) => {
    if (typeof args[0] === 'string') {
      args[0] = _id + args[0]
    } else {
      args.unshift(_id)
    }
    return _console[method].call(console, ...args)
  }
}

console.log = createLog('log', 'RENDER', 'green')
console.info = createLog('info', 'RENDER', 'green')
console.debug = createLog('debug', 'RENDER', 'green')
console.error = createLog('error', 'RENDER', 'red')

export const log = (data: any, id: string, color: typeof Color & keyof typeof chalk) => {
  const _id = chalk.bold[color](`[${id}]`)
  if (data) {
    if (typeof data === 'string') {
      const lines: string[] = data.split(/\r?\n/)
      lines.forEach((line) => {
        _console.log(_id, line)
      })
    } else {
      _console.log(`${_id} %o`, data)
    }
  }
}

export const isElectronModule = (id: string) => {
  return electron_modules.has(id)
}

export const escape = (str: string) => str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&')

export const hashString = (source: string, length = 8) => {
  return createHash('sha1').update(source).digest('hex').substring(0, length)
}

export const getHashedIdentifier = (source: string, prefix = '_') => {
  const hashStr = createHash('sha1').update(source).digest('base64url').replace(/-/g, '$')
  return `${prefix}${hashStr}`
}

let i = 0
export const getUniqueIdentifier = (prefix = '_') => {
  const hashStr = createHmac('sha1', String(Math.random()))
    .update(String(Date.now()))
    .digest('base64url')
    .replace(/-/g, '$')
  return `${prefix}${hashStr}${(i++).toString(16)}`
}

export const toArray = <T = any>(data: any): T[] => {
  if (Array.isArray(data)) return data
  if (data == null) return []
  if (typeof data !== 'object') return [data]
  if (Symbol.iterator in data || 'length' in data) return Array.from(data as any)
  return [data]
}

export const relativePath = (to: string) => {
  return normalizePath(path.relative(CWD, to))
}

export const safeRequire = (id: string) => {
  try {
    return require(id)
  } catch (error) {}
}

export const safeRequireResolve = (id: string) => {
  try {
    return require.resolve(id)
  } catch (error) {}
}

export const packageJson = safeRequire(path.resolve('package.json'))

const electron_modules = new Set([
  'electron',
  ...difference(builtinModules, [
    ...Object.keys(packageJson?.dependencies ?? {}),
    ...Object.keys(packageJson?.devDependencies ?? {}),
  ]),
])
